package com.larsgard.gedviewer.server.storage;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.Extent;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;

import org.datanucleus.exceptions.NucleusUserException;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.larsgard.gedviewer.server.storage.entities.PersitenceCapable;

public abstract class Storage<U extends PersitenceCapable> {
    static Logger log = Logger.getLogger(Storage.class.getCanonicalName());

    public Key createKey(U clazz, String id, Key entityGroupKey) {
	if (entityGroupKey != null) {
	    return KeyFactory.createKey(entityGroupKey, clazz.getClass().getSimpleName(), id);
	} else {
	    return KeyFactory.createKey(clazz.getClass().getSimpleName(), id);
	}
    }

    protected int storeObjects(List<U> objectList) {

	int storedObjectsCount = 0;

	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();
	Transaction tx = pm.currentTransaction();
	try {
	    tx.begin();
	    pm.makePersistentAll(objectList);
	    tx.commit();
	} catch (Exception e) {
	    if (tx.isActive()) {
		tx.rollback();
	    }
	    log.severe("Failed when storing objects , e.message was " + e.getMessage());
	    log.severe(e.toString());
	    e.printStackTrace();
	} finally {
	    pm.close();
	}
	return storedObjectsCount;
    }

    protected U storeObject(U objectToStore) {
	// if (objectToStore.getKey() == null) {
	// log.warning("Key not set for storage-object, not storing{" + objectToStore.getClass()
	// + "}");
	// throw new KeyNotSetException(objectToStore.getClass());
	// }

	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();
	Transaction tx = pm.currentTransaction();
	U result = null;
	try {
	    tx.begin();
	    result = pm.makePersistent(objectToStore);
	    tx.commit();
	} catch (Exception e) {
	    if (tx.isActive()) {
		tx.rollback();
	    }
	    log.severe("Failed when storing object of type " + objectToStore.getClass()
		    + ", e.message was " + e.getMessage());
	} finally {
	    pm.close();
	}
	return result;
    }

    @SuppressWarnings("unchecked")
    protected U getObjectWithIdentySet(U objectWithIdentitySet) throws KeyNotSetException {
	if (objectWithIdentitySet.getKey() == null) {
	    throw new KeyNotSetException(objectWithIdentitySet.getClass());
	}
	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();

	Key key = objectWithIdentitySet.getKey();

	U result = null;
	// Transaction tx = pm.currentTransaction();
	try {
	    // tx.getNontransactionalRead();
	    result = (U) pm.getObjectById(objectWithIdentitySet.getClass(), key);
	} catch (JDOObjectNotFoundException e) {
	    result = null;
	    log.warning(e.getMessage());
	    log.warning("Could not find [" + objectWithIdentitySet.getClass().getName()
		    + "] for id [" + key + "]");
	} finally {
	    try {

		pm.close();
	    } catch (Exception e) {
		log.severe("Failed when getting object of type " + objectWithIdentitySet.getClass()
			+ ", e.message was " + e.getMessage());

	    }
	}

	return result;
    }

    @SuppressWarnings("unchecked")
    protected ArrayList<U> getAllObjects(Class uclass) {
	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();
	ArrayList<U> results = new ArrayList<U>();

	Extent extent = pm.getExtent(uclass, false);
	for (Object obj : extent) {
	    results.add((U) obj);
	}
	extent.closeAll();
	pm.close();

	return results;
    }

    protected Key createUniqueKey(String keyValue, U storedObject) {
	Key key = KeyFactory.createKey(storedObject.getClass().getSimpleName(), keyValue);
	return key;
    }

    protected boolean objectAlreadyExists(U object) throws KeyNotSetException {
	if (object.getKey() == null) {
	    return false;
	}
	U existingObject = getObjectWithIdentySet(object);
	if (existingObject != null) {
	    return true;
	} else {
	    return false;
	}
    }

    protected void deleteAllObjects(U objectTypeToDelete) {

	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();
	Transaction tx = pm.currentTransaction();
	try {
	    Query query = pm.newQuery(objectTypeToDelete.getClass());
	    tx.begin();
	    query.deletePersistentAll();
	    query.execute();
	    tx.commit();
	} catch (NucleusUserException e) {
	    if (tx.isActive()) {
		tx.rollback();
		log.severe("Failed when deleting object of type " + objectTypeToDelete.getClass()
			+ " with key[" + objectTypeToDelete.getKey() + "]" + ", e.message was "
			+ e.getMessage());

	    }
	    throw e;
	} finally {
	    try {
		pm.close();
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
    }

    @SuppressWarnings("unchecked")
    protected void deleteObject(U objectToDelete) {
	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();

	Transaction tx = pm.currentTransaction();
	try {
	    tx.begin();
	    U persistentObject = (U) pm.getObjectById(objectToDelete.getClass(), objectToDelete
		    .getKey());
	    pm.deletePersistent(persistentObject);
	    tx.commit();
	} catch (Exception e) {
	    if (tx.isActive()) {
		tx.rollback();
	    }
	    log.severe("Failed when deleting object of type " + objectToDelete.getClass()
		    + " with key [" + objectToDelete.getKey() + "]" + ", e.message was "
		    + e.getMessage());
	} finally {
	    pm.close();
	}
    }

    @SuppressWarnings("unchecked")
    protected ArrayList<U> getObjectList(Class objectType, int fromRange, int toRange,
	    String ordering, String filter) {
	ArrayList<U> result = new ArrayList<U>();
	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();

	// Transaction tx = pm.currentTransaction();
	try {
	    // tx.begin();
	    // tx.setNontransactionalRead(true);
	    Query query = pm.newQuery(objectType);
	    query.setRange(fromRange, toRange);
	    if (ordering != null) {
		query.setOrdering(ordering);
	    }
	    if (filter != null) {
		query.setFilter(filter);
	    }
	    List<U> tmpresult = (List<U>) query.execute();
	    for (U u : tmpresult) {
		result.add(u);
	    }
	    // tx.commit();
	} catch (Exception e) {
	    log.severe("Failed when doing query  on  " + objectType.getClass() + ", e.message was "
		    + e.getMessage());
	} finally {
	    try {
		// if (tx.isActive()) {
		// tx.rollback();
		pm.close();
		// }
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
	return result;
    }

    public void deleteObjects(List<U> objectlist) {
	PersistenceManager pm = PersistenceManagerFactory.getInstance().getPersistenceManager();

	Transaction tx = pm.currentTransaction();
	try {
	    tx.begin();
	    // pm.makePersistentAll(objectlist);
	    pm.deletePersistentAll(objectlist);
	    tx.commit();
	} catch (Exception e) {
	    if (tx.isActive()) {
		tx.rollback();
	    }
	    log.severe("Failed when deleting objectilst  e.message was " + e.getMessage());
	} finally {
	    pm.close();
	}
    }

}
